home *** CD-ROM | disk | FTP | other *** search
/ Carousel / CAROUSEL.cdr / mactosh / utilprn / hpdeskje.sit / HPDJet ƒ / XPrint.c < prev   
C/C++ Source or Header  |  1989-04-02  |  33KB  |  1,038 lines

  1. /* 02.04.1989  amn  (latest edit) */
  2.  
  3. /* XPrint.c  -  low-level printer driver for Macintosh and HP DeskJet. */
  4.  
  5. /* Compiles into 'DRVR' resource, id 2, name '.XPrint'. */
  6. /* LightspeedC compiler produces also a 'DATA' resource, id -16320 */
  7. /* which contains driver's global variables.  A handle to these is placed */
  8. /* into driver's device control entry in 'dCtlStorage' (IM II-190). */
  9.  
  10. /* These resources are placed into the printer resource file */
  11. /* 'HP DeskJet', type 'PRER', creator 's89^' as */
  12. /* 'DRVR' -8192 '.XPrint' and */
  13. /* 'PREC' -8192. */
  14.  
  15. /* The 'Chooser' desk accessory changes the name of the active printer resource file */
  16. /* in the 'STR ' -8192 in 'System' file. */
  17.  
  18. /* We suppose drivers shouldn't do 'SetResLoad(FALSE)'. */
  19. /* The application currently running may have decided to do so. */
  20. /* This is why we try to call 'LoadResource()' even directly after 'GetResource()'. */
  21. /* LightspeedC driver header code seems to call 'SetResLoad(TRUE)' when processing */
  22. /* an open call.  We hope this doesn't interfere seriously with applications. */
  23.  
  24. /* Authors:  Ari Mujunen (amn@hutcs.hut.fi) and Olli Arnberg (oar@hutcs.hut.fi). */
  25. /* Copyright Ari Mujunen, Olli Arnberg 1989. */
  26. /* You may redistribute the driver (=printer resource file, source files, */
  27. /* documentation file(s), and the file 'Copyright and Source Offer') */
  28. /* only _non-commercially_ and _in its entirety_. */
  29. /* See the file 'Copyright and Source Offer' and/or documentation for details. */
  30. /* Acknowledgements:  Special thanks to Mr. Earle R. Horton for his 'Daisy' */
  31. /* daisywheel printer driver and its source code published in 'MacTutor', Nov-Dec 1987. */
  32. /* This driver served as a basis and inspiration for our work.  It also */
  33. /* proofed that a Macintosh printer driver can be done despite the lack of */
  34. /* documentation from Apple. */
  35.  
  36. /* Change history: */
  37. /* Version  When        Who      Why */
  38. /* 2.0      12.11.1988  amn      Original rewrite. */
  39. /*          13.11.1988  amn      Getting our globals when we recognize they are not ours. */
  40. /*          14.11.1988  amn      Verifying why 'FKEY' 4 opens us _two_ 1st times. */
  41. /*          15.11.1988  amn      --"-- */
  42. /*          16.11.1988  amn      Found out that we killed Device Manager's dCtlFlags */
  43. /*                               low-order byte by storing directly to it. */
  44. /*                               Cleaning off 'asm's. */
  45. /*          18.11.1988  amn      Preparing to use fewer globals. */
  46. /*          19.11.1988  amn      Globals reduced, comments checked. */
  47. /*          20.11.1988  amn      Getting the volume of 'System' file. */
  48. /*          21.11.1988  amn      Names in 'prglobal.h' changed. */
  49. /*          24.11.1988  amn      Read printed listing... */
  50. /*          25.11.1988  amn      Checking the magic number in our globals correctly. */
  51. /*          26.11.1988  amn      Set 'dCtlFlags' every time we are opened. */
  52. /*                               The actual error was an extraneous semicolon */
  53. /*                               in the 'if' statement opening serial driver. */
  54. /*          27.11.1988  amn      Searching for a memory overwrite problem. */
  55. /*                               Found it: used a global before they were read from resource. */
  56. /*          12.12.1988  amn      Grouping of consecutive empty bit lines to one DJ command. */
  57. /*          03.02.1989  amn      Moving RESID-constants to prglobals.h. */
  58. /*          06.02.1989  amn/oar  Dropping compression when in LaserJet compatibility mode. */
  59. /*          25.02.1989  amn      Searching why this still compresses while in LJC-mode. */
  60. /*                               Don't know, now it definitely does not compress in LJC-mode. */
  61. /*          24.03.1989  amn      Adding detection of dirty and non-dirty bit lines in LJC-mode. */
  62. /*          25.03.1989  amn/oar  Removing global resource file refNums. */
  63. /*                               Does not deallocate globals when closed: MF-compatibility. */
  64. /*          26.03.1989  amn      Reload 'Stng' and 'STR#' every time opened */
  65. /*                               so Chooser changes take effect immediately. */
  66. /*          28.03.1989  amn      Settable printer origin. */
  67. /* 2.1      02.04.1989  amn,oar  Released version. */
  68.  
  69.  
  70. #include "common_mac_includes.h"
  71.  
  72. /* Mac OS includes specific to this module: */
  73. #include <ResourceMgr.h>
  74. /* #include <Environs.h>  we don't have this feature. */
  75. #include <SerialDvr.h>
  76. /* #include <HFS.h>: (needs FileMgr.h); we don't have this feature. */
  77.  
  78.  
  79. #define DRIVER_MODULE
  80. #include "prglobals.h"
  81.  
  82.  
  83. /* Device driver routine numbers.  LightspeedC passes this number as a routine */
  84. /* selector value in the third integer parameter to the function 'main'. */
  85. #define OPEN 0
  86. #define PRIME 1
  87. #define CONTROL 2
  88. #define STATUS 3
  89. #define CLOSE 4
  90.  
  91. /* Control command codes (csCode) for the serial driver. */
  92. #define SERIAL_RESET 8
  93. #define SERIAL_HANDSHAKE 10
  94.  
  95. /* Xon/Xoff characters to be used when handshaking is requested from the serial driver. */
  96. #define XON_CHAR ((char)17)
  97. #define XOFF_CHAR ((char)19)
  98.  
  99.  
  100. /* Font characterization table for use by FontManager and XPrint. */
  101. typedef struct {
  102.     int vres;
  103.     int hres;
  104.     SignedByte bold[3];
  105.     SignedByte italic[3];
  106.     SignedByte notused[3];
  107.     SignedByte outline[3];
  108.     SignedByte shadow[3];
  109.     SignedByte condensed[3];
  110.     SignedByte extended[3];
  111.     SignedByte underline[3];
  112. } tFontCharacterizationTable, *ptFontCharacterizationTable, **htFontCharacterizationTable;
  113.  
  114.  
  115. /* Function prototypes for every function in this source file.*/
  116.  
  117. /* Device driver entry point.  LightspeedC generates code to call this function. */
  118. int main(ptPrParam, DCtlPtr, int);
  119.  
  120. /* Routine which opens our device driver.  Get our global variables */
  121. /* as a resource 'PREC', id -8192, load them into memory and lock them down. */
  122. /* Load settings resource 'Stng', id -4080 into global struct 'currentSettings'. */
  123. OSErr driverOpen(DCtlPtr);
  124.  
  125. /* Open ourselves (=printer resource file) and */
  126. /* return the refNum of our printer resource file. */
  127. int openPrinterResourceFile(DCtlPtr);
  128.  
  129. /* Load 'Sntg' and 'STR#' settings into globals. */
  130. OSErr reloadSettings(void);
  131.  
  132. /* Low-level driver must be capable of sending printer controls as FF, LF etc. in */
  133. /* a device-independent manner.  These routines fetch the actual control strings from */
  134. /* settings resource 'STR#', id -4080. */
  135. void setControlStrings(StringHandle);
  136. void copyPascalString(StringPtr, StringPtr);
  137. void decodeCaretsToControlCharacters(StringPtr);
  138.  
  139. /* Actual printing is done using Mac serial driver called '.AOut'/'.BOut'. */
  140. /* The driver is never closed, because the old 64K ROM serial driver hangs the */
  141. /* machine when closed.  Hardware (CTS) handshaking is strongly recommended. */
  142. OSErr openSerialDriverAndConfigureIt(void);
  143. OSErr openDriverByName(StringPtr);
  144. void reconfigureSerialPort(void);
  145.  
  146. /* Routines that send characters to the serial line and the printer. */
  147. OSErr sendPrinterControl(long);
  148. OSErr sendString(StringPtr);
  149.  
  150. /* Routines that compress and send a bitmap to the printer. */
  151. OSErr printBitMap(int, BitMap, Rect);
  152. void compressBitLine(int, StringPtr, int *, StringPtr, Boolean *);
  153.  
  154.  
  155. /* Function definitions. */
  156.  
  157. int
  158. main(
  159.     pb,  /* ==> parameter block with which we were called*/
  160.     dce,  /* ==> device control entry */
  161.     routineSelector  /* which of OPEN, PRIME, CONTROL, STATUS, CLOSE */
  162. )
  163. ptPrParam pb;
  164. DCtlPtr dce;
  165. int routineSelector;
  166. {
  167. /* Check to make sure our data area was allocated. We cannot image how this code */
  168. /* can be in memory unless the OPEN routine is called first. */
  169. /* Note that we cannot use any of LightspeedC's "global" variables */
  170. /* until after we get our storage from the resource file and lock it down, */
  171. /* making A4 point to it. This probably does the same thing that the prolog code */
  172. /* attempts to do with the original 'DATA' resource created by LightspeedC. */
  173.     
  174.     OSErr retCode;
  175.     
  176.     if (!((routineSelector == OPEN) || (routineSelector == CLOSE))) {
  177.         if (dce->dCtlStorage == nil)
  178.             SysError(25);  /* somebody managed to call CONTROL/STATUS before OPEN */
  179.         if ((*(long *)(*dce->dCtlStorage)) != OUR_MAGIC_NUMBER)
  180.             SysError(25);  /* it is not ours */
  181.         
  182.         /* Now we have our globals (dce->dCtlStorage), ensure lockedness: */
  183.         HLock(dce->dCtlStorage);
  184.         
  185.         /* Make LSC use the locked storage as its globals: */
  186.         {
  187.             Ptr pointer;
  188.             
  189.             /* Strip off Memory Manager bits. */
  190.             pointer = (Ptr)((long)(*(dce->dCtlStorage)) & (Lo3Bytes));
  191.             asm {
  192.                 movea.l pointer,a4    ;; LSC uses A4 as the base for driver globals
  193.             }
  194.         }
  195.     }  /* end if other than CLOSE */
  196.     
  197.     switch (routineSelector) { /* dispatch */
  198.     
  199.         case OPEN:
  200.             /* Applications seem to call this often to ensure our driver is open. */
  201.             /* 'FKEY' 4, screen print, opens us after it has noticed that the first */
  202.             /* screen dump call failed because the driver isn't open (and not in memory). */
  203.             /* The Device Manager of System 2.0 calls us despite we are open. */
  204.             /* System 4.2 does not seem to call any longer. */
  205.             
  206.             if (dce->dCtlStorage != nil)
  207.                 if ((*(long *)(*dce->dCtlStorage)) != OUR_MAGIC_NUMBER) {
  208.                     DisposHandle(dce->dCtlStorage);  /* is is not ours, dispose it */
  209.                     dce->dCtlStorage = nil;
  210.                 }
  211.             if (dce->dCtlStorage == nil) {
  212.                 if ((retCode = driverOpen(dce)) != noErr) {
  213.                     PrintErr = retCode;
  214.                     (void)CloseDriver(dce->dCtlRefNum);  /* calls recursively ??? */
  215.                     return(retCode);
  216.                 }
  217.             }
  218.             
  219.             /* If we are reopened by Chooser, the (possibly) changed settings */
  220.             /* must be reloaded. */
  221.             if ((retCode = reloadSettings()) != noErr)
  222.                 return(retCode);
  223.             reconfigureSerialPort();
  224.             
  225.             dce->dCtlFlags = dCtlEnable | dStatEnable | (dce->dCtlFlags & 0x00FF);
  226.             /* Locking is not requested, BUT just in case high-level printing code */
  227.             /* manages to get memory by purging our driver doesn't seem fair... */
  228.             /* Perhaps the resource bit 'Purgeable' should be cleared ??? */
  229.             
  230.             /* Printer driver version. */
  231.             dce->dCtlQHdr.qFlags = PRINTING_MANAGER_VERSION;  /* Yes, it seems to be there !!! */
  232.             
  233.             PrintErr = noErr;  /* Somebody may bother to check this. */
  234.             break;  /* case OPEN */
  235.         
  236.         case PRIME:
  237.             /* Read/write calls not for low-level printer drivers. */
  238.             break;
  239.         
  240.         case CONTROL:
  241.             switch (pb->csCode) {
  242.                 /* 'pb->csCode' gives opcode, and we switch on */
  243.                 /* it to perform low-level Printing calls. */
  244.                 
  245.                 case iPrBitsCtl: /* Send bitmap to printer. */
  246.                     {
  247.                         BitMap *pBitMapPtr;
  248.                         Rect *pPortRectPtr;
  249.                         int resolution;
  250.                         
  251.                         pBitMapPtr = (BitMap *)(pb->lParam1);
  252.                         
  253.                         pPortRectPtr = (Rect *)(pb->lParam2);
  254.                         
  255.                         if (pb->lParam3 == lScreenBits)
  256.                             resolution = 100;
  257.                         else
  258.                         if (pb->lParam3 == lPaintBits)
  259.                             resolution = 75;
  260.                         else
  261.                             resolution = (int)(pb->lParam3);
  262.                         
  263.                         return( printBitMap(
  264.                             resolution, 
  265.                             *pBitMapPtr,
  266.                             *pPortRectPtr)
  267.                         );
  268.                     }
  269.                     break;
  270.                 
  271.                 case iPrIOCtl: /* Text streaming. */
  272.                     serialParameterBlock.ioParam.ioBuffer = (Ptr)pb->lParam1;
  273.                     serialParameterBlock.ioParam.ioReqCount = pb->lParam2;
  274.                     return(PBWrite(&serialParameterBlock, FALSE));
  275.                     break;
  276.                 
  277.                 case iPrEvtCtl: /* Screen print. */
  278.                     {
  279.                         BitMap *screenBitsPtr;
  280.                         
  281.                         /* Fetch the QuickDraw global var. screenBits */
  282.                         screenBitsPtr = (BitMap *)(*((Ptr *)(CurrentA5))
  283.                             - (Ptr)(5*sizeof(Pattern) + sizeof(Cursor) + sizeof(BitMap)));
  284.                         return( printBitMap(
  285.                             currentSettings.screenDumpResolution, 
  286.                             *screenBitsPtr, 
  287.                             screenBitsPtr->bounds)
  288.                         );
  289.                         
  290.                         /* We should heed the 'pb->lParam1' being == 'lPrEvtTop' */
  291.                         /* which means only the top window should be printed. */
  292.                         /* Then we should remember to HideCursor() before print. */
  293.                         /* Currently our bit map print cannot use .left and .right */
  294.                         /* of portRect passed to it, only .top and .bottom. */
  295.                     }
  296.                     break;
  297.                     
  298.                 case iPrDevCtl: /* CR, LF, FF, etc */
  299.                     return(sendPrinterControl(pb->lParam1));
  300.                     break;
  301.                     
  302.                 case iFMgrCtl:
  303.                     /* Font table modify request. pb->lParam1 is a
  304.                     pointer to an FMInput record. (IM says we get an
  305.                     FMOutputPtr here but MacsBug says we get a pointer to
  306.                     FMInput. I go with MacsBug.)  In addition, the
  307.                     integer following pb->lParam1 contains our driver
  308.                     reference number and a private byte we can use
  309.                     internally. The application first does something
  310.                     to determine the characteristics of fonts in our
  311.                     printing GrafPort. The FontManager calls our 
  312.                     status routine with csParam = 8 and we give it a
  313.                     "font characterization table". The FontManager
  314.                     selects a font, and calls our control routine to 
  315.                     confirm the choice. */
  316.                     {
  317.                     /* Currently we do no modifications. */
  318.                     }
  319.                     break;
  320.                     
  321.                 goodBye:
  322.                     /* Application heap is about to be reinitialized. */
  323.                     /* Currently the is nothing special to be done: */
  324.                     /* All allocated memory is in the application heap */
  325.                     /* and the ROM serial driver must not be closed. */
  326.                     /* If you add some code here, remember to change dCtlFlags */
  327.                     /* in 'driverOpen()'. */
  328.                     break;
  329.                 
  330.                 killCode:
  331.                     /* Killing all pending (serial) I/O is requested. */
  332.                     /* Should we be able to do it ??? Isn't all serial I/O */
  333.                     /* done synchronously ???  Don't WE do it synchronously ??? */
  334.                     break;
  335.                 
  336.                 default:
  337.                     break;
  338.                 }
  339.             break;   /* case CONTROL */
  340.             
  341.         case STATUS:
  342.             switch (pb->csCode) {
  343.                 /* pb->csCode gives opcode, and we switch on it to */
  344.                 /* perform low-level Printing calls. */
  345.                 case iFMgrCtl:
  346.                     /* Font Manager font characterization table request. */
  347.                     /* 'pb->lParam1' is a pointer to an area in memory */
  348.                     /* in which to put a copy of the information */
  349.                     /* describing our font capabilities/directions to QuickDraw. */
  350.                     /* 'pb->lParam2' has an integer in its high order word */
  351.                     /* which contains the device information from printing */
  352.                     /* port's 'grafDevice' field.  This is set from print record */
  353.                     /* (print.prInfoPT.iDev and print.prStl.wDev) */
  354.                     /* by high-level printing code -- there it was */
  355.                     /* set by style dialog (PDEF 4). */
  356.                     /* High order byte contains our driver's reference number so */
  357.                     /* font manager could call our driver.  Low order byte contains */
  358.                     /* our resolution information which we use to select the correct */
  359.                     /* table from our resources. */
  360.                     {
  361.                         htFontCharacterizationTable myFonts;
  362.                         int resolution;
  363.                         
  364.                         resolution = (int)(pb->lParam2 >> 16) & RES_MASK;
  365.                         myFonts = (htFontCharacterizationTable)GetResource(
  366.                             'FCHT',
  367.                             RESID_OWNED_BY_PDEF+resolution
  368.                         );
  369.                         if (myFonts == nil)
  370.                             return(ResError());
  371.                         LoadResource(myFonts);
  372.                         *((ptFontCharacterizationTable)pb->lParam1) = **myFonts;
  373.                     }
  374.                     break;
  375.                 default:
  376.                     break;
  377.                 }
  378.             break;  /* case STATUS */
  379.             
  380.         case CLOSE:
  381.             /* Applications seldom close our driver: to do this, they should call */
  382.             /* 'PrDrvrClose' in addition to PrClose (see IM II-157). */
  383.             /* Closing the driver seems to be reserved for 'Chooser' */
  384.             /* when it changes the active printer resource file. */
  385.             
  386.             /* Don't reference any driver global variables here: */
  387.             /* We don't know if they are allocated or not. */
  388.             
  389.             /* In MultiFinder we cannot know if another application */
  390.             /* needs us despite another wants to close. */
  391.             /* So we don't deallocate our globals: this has the unfortunate */
  392.             /* side effect:  using Chooser leaves a block of them floating. */
  393.             /***
  394.             if (dce->dCtlStorage != nil) {
  395.                 (*(long *)(*dce->dCtlStorage)) = 0L;  / * destroy our magic number * /
  396.                 DisposHandle(dce->dCtlStorage);
  397.                 dce->dCtlStorage=nil;
  398.             }
  399.             ***/
  400.             break;
  401.             
  402.         default:
  403.             /* Device drivers have only OPEN, PRIME, CONTROL, STATUS, and CLOSE. */
  404.             break;
  405.     }  /* switch routineSelector */
  406.     
  407.     /* Changing the low-memory global 'PrintErr' is reserved for high-level printing */
  408.     /* code -- don't change it here except for OPEN call. */
  409.     /* 'PrintErr' must retain its value (as set for example when writing to spool file) */
  410.     /* despite calls to low-level driver. */
  411.     
  412.     return(noErr);
  413. }  /* main */
  414.  
  415.  
  416. OSErr
  417. driverOpen(dce)
  418. DCtlPtr dce;
  419. {
  420.     OSErr retCode;
  421.     
  422.     if (openPrinterResourceFile(dce) == -1)
  423.         return(dInstErr);  /* 'couldn't find driver in resource file' ??? */
  424.     
  425.     /* Get driver's initialized control storage from printer resource file, */
  426.     /* lock it, and make A4 point to it thus LSC can use variables in it like globals. */
  427.     
  428.     if ((dce->dCtlStorage = (Handle)GetResource('PREC', RESID_OWNED_BY_PDEF)) == nil)
  429.         return(ResError());
  430.     
  431.     {
  432.         Handle storage;
  433.         Ptr pointer;
  434.         
  435.         storage = dce->dCtlStorage;
  436.         LoadResource(storage);
  437.         DetachResource(storage);
  438.         HNoPurge(storage);
  439.         HLock(storage);
  440.         
  441.         /* Strip off Memory Manager bits from the pointer. */
  442.         pointer = (Ptr)((long)(*storage) & (Lo3Bytes));
  443.         asm {
  444.             movea.l pointer,a4    ;; LSC uses A4 as the base for driver globals
  445.         }
  446.     }
  447.     
  448.     /* Now our globals are accessible. */
  449.     
  450. /*    theWorld = myWorld; ??? */
  451.  
  452.     if ((retCode = reloadSettings()) != noErr)
  453.         return(retCode);
  454.         
  455.     if ((retCode = openSerialDriverAndConfigureIt()) != noErr)
  456.         return(retCode);
  457.     
  458.     /* High-level driver (PDEF0&5) communicates with low-level (XPrint) */
  459.     /* using this initialized global parameter block 'xPrintParameterBlock'. */
  460.     /* Now we initialize the pointer to the name of the driver: */
  461.     xPrintParameterBlock.ioNamePtr = xPrintName;
  462.         /*== #define sPrDrvr from <PrintMgr.h>, */
  463.         /* initialized in "prglobals.h". */
  464.             
  465.     return(noErr);
  466. }  /* driverOpen */
  467.  
  468.  
  469. int
  470. openPrinterResourceFile(dce)
  471. DCtlPtr dce;
  472. /* Find the system folder and ensure our printer resource file is open. */
  473. /* Fill in a global SysEnvRec, which might be useful for other stuff. */
  474. {
  475. /***     SysEnvRec myWorld; ***/
  476.     StringHandle ourname;
  477.     int theRefNum;
  478.     OSErr retCode;
  479.     
  480.     /* Should not be called if we have something allocated. */
  481.     if (dce->dCtlStorage != nil)
  482.         SysError(25);
  483.     
  484.     /* Find the name of our printer resource file.  It is in the System file. */
  485.     if ((ourname = (StringHandle)GetResource('STR ', RESID_OWNED_BY_PDEF)) == nil)
  486.         return(-1);
  487.     
  488.     LoadResource(ourname);
  489.     HLock(ourname);
  490. /***    SysEnvirons(1, &myWorld); ***/
  491. /***    if (myWorld.machineType >= envMachUnknown)
  492.         theRefNum = OpenRFPerm(*ourname, myWorld.sysVRefNum, fsCurPerm);
  493.     else ***/ {
  494.         int systemResFile;
  495.         
  496.         /* Get the actual reference number of system file. */
  497.         {
  498.             int saveResFile;
  499.             
  500.             saveResFile = CurResFile();
  501.             UseResFile(0);  /* System file, IM I-117. */
  502.             systemResFile = CurResFile();
  503.             UseResFile(saveResFile);
  504.         }
  505.         
  506.         {
  507.             int saveVolRefNum;
  508.             Str255 saveVolName;
  509.             int systemVolumeRefNum;
  510.             
  511.             /* Save current volume refNum. */
  512.             if (GetVol(&saveVolName, &saveVolRefNum) != noErr)  /* save current default volume */
  513.                 saveVolRefNum = 0;  /* We hope 0 is not a legal volume reference number. */
  514.             
  515.             /* Get the volume where system file resides. */
  516.             theRefNum = -1;
  517.             if (GetVRefNum(systemResFile, &systemVolumeRefNum) == noErr)
  518.                 if (SetVol(nil, systemVolumeRefNum) == noErr)  /* open in the same folder as System */
  519.                     theRefNum = OpenResFile(*ourname);
  520.             /* theRefNum == opened resource file refNum or -1 */
  521.             
  522.             if (saveVolRefNum != 0)
  523.                 SetVol(nil, saveVolRefNum);  /* restore current default volume */
  524.         }
  525.     }
  526.     HUnlock(ourname);
  527.     HPurge(ourname);
  528.     return(theRefNum);
  529. }  /* openPrinterResourceFile */
  530.  
  531.  
  532. OSErr
  533. reloadSettings()
  534. {
  535.     htSettings hSettings;
  536.     StringHandle hStrings;
  537.  
  538.     if ((hSettings = (htSettings)GetResource('Stng',RESID_OWNED_BY_PDEF)) == nil)
  539.         return(ResError());
  540.     LoadResource(hSettings);
  541.     HNoPurge(hSettings);
  542.     currentSettings = **hSettings;
  543.     HPurge(hSettings);
  544.     
  545.     if ((hStrings = (StringHandle)GetResource('STR#',RESID_OWNED_BY_CHOOSER_PACK)) == nil)
  546.         return(ResError());
  547.     LoadResource(hStrings);
  548.     HLock(hStrings);
  549.     setControlStrings(hStrings);
  550.     HUnlock(hStrings);
  551.     HPurge(hStrings);
  552. }  /* reloadSettings */
  553.  
  554.  
  555. void
  556. setControlStrings(myStrings)
  557. StringHandle myStrings;
  558. {
  559.     StringPtr strptr;
  560.  
  561.     copyPascalString( (strptr =  (*myStrings)+2) , prlfstr);
  562.     copyPascalString( (strptr += (*strptr) + 1)  , prinitstr);
  563.     copyPascalString( (strptr += (*strptr) + 1)  , prtopstr);
  564.     copyPascalString( (strptr += (*strptr) + 1)  , preopstr);
  565.     copyPascalString( (strptr += (*strptr) + 1)  , preofstr);
  566.     decodeCaretsToControlCharacters(prlfstr);
  567.     decodeCaretsToControlCharacters(prinitstr);
  568.     decodeCaretsToControlCharacters(prtopstr);
  569.     decodeCaretsToControlCharacters(preopstr);
  570.     decodeCaretsToControlCharacters(preofstr);
  571. }  /* setControlStrings */
  572.  
  573.  
  574. void
  575. copyPascalString(src, dst)
  576. StringPtr src, dst;
  577. /* This is for copying PASCAL strings (simple) */
  578. {
  579.     asm {
  580.         clr.l d0
  581.         move.l src,a0
  582.         move.l dst,a1
  583.         move.b (a0),d0
  584.         loop:
  585.         move.b (a0)+,(a1)+
  586.         dbra d0,@loop
  587.     }
  588. }  /* copyPascalString */
  589.  
  590.  
  591. void
  592. decodeCaretsToControlCharacters(str)
  593. StringPtr str;
  594. {
  595.     register unsigned char i, count;
  596.     
  597.     count = 1;
  598.     for (i=0; str[0]-i++;) {
  599.         if (str[i] == '^')
  600.             str[count] = 31 & str[++i];
  601.         else
  602.             str[count] = str[i];
  603.         count++;
  604.     }
  605.     str[0] = count - 1;
  606. }  /* decodeCaretsToControlCharacters */
  607.  
  608.  
  609. OSErr
  610. openSerialDriverAndConfigureIt()
  611. /* Open serial driver and configure it.  Use low-level ROM routines. */
  612. {
  613.     OSErr retCode;
  614.     
  615.     switch (currentSettings.port) {
  616.         case 0: /* modem port */
  617.             retCode = openDriverByName((StringPtr)"\p.AOut");
  618.             break;
  619.         case 1: /* printer port */
  620.             retCode = openDriverByName((StringPtr)"\p.BOut");
  621.             break;
  622.     }
  623.     if (retCode != noErr)
  624.         return(retCode);
  625.         
  626.     /* Set up the I/O parameter block for writing to the serial driver. */
  627.     reconfigureSerialPort();
  628.     
  629.     return(noErr);
  630. }  /* openSerialDriverAndConfigureIt */
  631.  
  632.  
  633. OSErr
  634. openDriverByName(name)
  635. Str255 name;
  636. /* Open a driver by name. ROM, Ram, serial driver? Who cares? */
  637. {
  638.     serialParameterBlock.ioParam.ioNamePtr = name;
  639.     serialParameterBlock.ioParam.ioCompletion = nil;
  640.     serialParameterBlock.ioParam.ioPermssn = fsCurPerm;
  641.     return(PBOpen(&serialParameterBlock, FALSE));
  642. }  /* openDriverByName */
  643.  
  644.  
  645. /* Set up the I/O parameter block for writing to the serial driver. */
  646. /* A control call resets the baud rate, another the handshaking method. */
  647. void
  648. reconfigureSerialPort()
  649. {
  650.         ParmBlkPtr    pb;
  651.         int serialDriverRefNum;
  652.         int    serialConfigWord;
  653.         
  654.         switch (currentSettings.port) {
  655.             case 0: /* modem port */
  656.                 serialDriverRefNum = AoutRefNum;
  657.                 break;
  658.             case 1: /* printer port */
  659.                 serialDriverRefNum = BoutRefNum;
  660.                 break;
  661.         }
  662.     
  663.         pb = &serialParameterBlock;
  664.         pb->ioParam.ioRefNum = serialDriverRefNum;
  665.         pb->ioParam.ioCompletion = nil;
  666.         
  667.         ((cntrlParam *)pb)->csCode = SERIAL_RESET;
  668.         serialConfigWord = data8 + noParity + stop20;
  669.         serialConfigWord += currentSettings.baud;
  670.         ((cntrlParam *)pb)->csParam[0] = serialConfigWord;
  671.         (void)PBControl(pb, FALSE);  /*  Always returns 'noErr' according to IM II-251 */
  672.         
  673. #define shake ((SerShk*)&((cntrlParam*)pb)->csParam[0])
  674.         shake->errs = FALSE;
  675.         shake->evts = FALSE;
  676.         shake->fDTR = FALSE;
  677.         shake->fInX = FALSE;
  678.     /*     if (currentSettings.XonXoff && (theWorld.machineType) >= envMachUnknown)) {  ???
  679.             shake->fXOn = TRUE;
  680.             shake->fCTS = FALSE;
  681.             shake->xOn = XON_CHAR;
  682.             shake->xOff = XOFF_CHAR;
  683.         }
  684.         else { */
  685.             shake->fXOn = FALSE;
  686.             shake->fCTS = TRUE;
  687.     /*     } */
  688.         ((cntrlParam *)pb)->csCode = SERIAL_HANDSHAKE;
  689.         (void)PBControl(pb, FALSE);  /*  Always returns 'noErr' according to IM II-251 */
  690. #undef shake
  691.  
  692.         pb->ioParam.ioPosMode = 0;
  693.         pb->ioParam.ioPosOffset = 0;
  694. }  /* reconfigureSerialPort */
  695.  
  696.  
  697. OSErr
  698. sendPrinterControl(code)
  699. long code;
  700. /* printer device control calls */
  701. {
  702.     switch(code) {
  703.         case lPrReset:
  704.             return(sendString(prinitstr));
  705.         case lPrPageEnd:
  706.             return(sendString(preopstr));
  707.         case lPrLineFeed:
  708.         case lPrLFSixth:
  709.         case lPrLFEighth:
  710.             return(sendString(prlfstr));
  711.         default:
  712.             return(controlErr);
  713.     }
  714. }  /* sendPrinterControl */
  715.  
  716.  
  717. OSErr
  718. sendString(string)
  719. StringPtr string;
  720. /* printer control string to serial driver */
  721. {
  722.     serialParameterBlock.ioParam.ioBuffer = (Ptr)(string+1);
  723.     serialParameterBlock.ioParam.ioReqCount = (long)(string[0]);
  724.     return(PBWrite(&serialParameterBlock, FALSE));
  725. }  /* sendString */
  726.  
  727.  
  728. OSErr
  729. printBitMap(resolution, dumpBitMap, dumpPortRect)
  730. int resolution;
  731. BitMap dumpBitMap;
  732. Rect dumpPortRect;
  733. {
  734.     Str255 numAsString;
  735.     int i;
  736.     OSErr retCode;
  737.     
  738.     int compressedLineLength;
  739.     unsigned char compressedBitLine[500];  /* ??? LineLength+(LineLength/128) */
  740.     
  741.     
  742.     /* Set bit map resolution */
  743.     switch (resolution) {
  744.         case 75:
  745.             retCode = sendString((StringPtr)"\p\033*t75R");
  746.             break;
  747.         case 100:
  748.             retCode = sendString((StringPtr)"\p\033*t100R");
  749.             break;
  750.         case 150:
  751.             retCode = sendString((StringPtr)"\p\033*t150R");
  752.             break;
  753.         case 300:
  754.             retCode = sendString((StringPtr)"\p\033*t300R");
  755.             break;
  756.         default:
  757.             retCode = controlErr;
  758.             break;
  759.     }
  760.     if (retCode != noErr)
  761.         return(retCode);
  762.     
  763.     /* Start graphics */    
  764.     if ((retCode = sendString((StringPtr)"\p\033*r0A")) != noErr)
  765.         return(retCode); 
  766.     
  767.     /* 
  768.     Here we must convert the bit image from Mac into 'Compaction Graphics Mode 2'.
  769.     
  770.     The mode is as follows:
  771.     
  772.         a repeat count (-1 to -127) followed by data byte to repeated
  773.             or
  774.         block length (0 to 127) followed by block length + 1 data bytes
  775.         
  776.     */
  777.     
  778.     /* Currently we use either uncompacted graphics mode 0 or */
  779.     /* compacted graphics mode 2. */
  780.     if (currentSettings.useOnlyPCLLevel3Features) {
  781.         retCode = sendString((StringPtr)"\p\033*b0M");
  782.     }
  783.     else {
  784.         retCode = sendString((StringPtr)"\p\033*b2M");
  785.     }
  786.     if (retCode != noErr)
  787.         return(retCode); 
  788.     
  789.     for (i=(dumpPortRect.top - dumpBitMap.bounds.top);
  790.         i<(dumpPortRect.bottom - dumpBitMap.bounds.top);
  791.         i++) {
  792.         
  793.         Boolean bitLineContainsAtLeastOneBlackPixel;
  794.         
  795.         /* compress */
  796.         compressBitLine(
  797.             dumpBitMap.rowBytes,
  798.             (StringPtr)(dumpBitMap.baseAddr + (i*(dumpBitMap.rowBytes))),
  799.             &compressedLineLength,
  800.             compressedBitLine,
  801.             &bitLineContainsAtLeastOneBlackPixel
  802.         );
  803.         
  804.         
  805.         if (bitLineContainsAtLeastOneBlackPixel)
  806.         /* PTT will test if LaserJet cad do positioning by 1/300": */
  807.         /***    || currentSettings.useOnlyPCLLevel3Features) ***/
  808.         {
  809.             /* Start of line */
  810.             if ((retCode = sendString((StringPtr)"\p\033*b")) != noErr)
  811.                 return(retCode); 
  812.             NumToString((long)compressedLineLength, numAsString);
  813.             if ((retCode = sendString(numAsString)) != noErr)  /* line length */
  814.                 return(retCode); 
  815.             if ((retCode = sendString((StringPtr)"\pW")) != noErr)
  816.                 return(retCode); 
  817.     
  818.             /* Send the string to printer. */        
  819.             serialParameterBlock.ioParam.ioBuffer = (Ptr)compressedBitLine;
  820.             serialParameterBlock.ioParam.ioReqCount = (long)compressedLineLength;
  821.             if ((retCode = PBWrite(&serialParameterBlock, FALSE)) != noErr)
  822.                 return(retCode);
  823.         }
  824.         else {
  825.             int numberOfConsecutiveEmptyBitLines;
  826.             
  827.             i++;
  828.             numberOfConsecutiveEmptyBitLines = 1;
  829.             while(TRUE) {
  830.                 compressBitLine(
  831.                     dumpBitMap.rowBytes,
  832.                     (StringPtr)(dumpBitMap.baseAddr + (i*(dumpBitMap.rowBytes))),
  833.                     &compressedLineLength,
  834.                     compressedBitLine,
  835.                     &bitLineContainsAtLeastOneBlackPixel
  836.                 );
  837.                 if (bitLineContainsAtLeastOneBlackPixel)
  838.                     break;
  839.                 i++;
  840.                 numberOfConsecutiveEmptyBitLines++;
  841.             }
  842.             
  843.             /* Skip empty lines using HP DeskJet specific command. */
  844.             if ((retCode = sendString((StringPtr)"\p\033*p+")) != noErr)
  845.                 return(retCode); 
  846.             NumToString((long)((300/resolution) * numberOfConsecutiveEmptyBitLines), numAsString);
  847.             if ((retCode = sendString(numAsString)) != noErr)  /* bit lines to skip */
  848.                 return(retCode); 
  849.             if ((retCode = sendString((StringPtr)"\pY")) != noErr)
  850.                 return(retCode); 
  851.                 
  852.             /* Continue processing (possible) non-empty line again. */
  853.             i--;
  854.         }
  855.  
  856.     }  /* for */
  857.     
  858.     /* End graphics */
  859.     return(sendString((StringPtr)"\p\033*rB"));
  860.     
  861. }  /* printBitMap */
  862.  
  863.  
  864. void
  865. compressBitLine(lineLength, bitLine, compressedLineLength, compressedBitLine, dirty)
  866. int    lineLength;
  867. StringPtr bitLine;
  868. int    *compressedLineLength;
  869. StringPtr compressedBitLine;
  870. Boolean *dirty;
  871. {
  872.     int        i, k, m, state;
  873.     Str255     String;
  874.  
  875.     if (currentSettings.useOnlyPCLLevel3Features) {
  876.         BlockMove(bitLine, compressedBitLine, (Size)lineLength);
  877.         *compressedLineLength = lineLength;  /* length does not change */
  878.         *dirty = FALSE;
  879.         for (i=0;i<lineLength;i++)
  880.             if (bitLine[i] != (unsigned char)0)
  881.                 *dirty = TRUE;
  882.         return;
  883.     }
  884.  
  885. #define INITIAL 0
  886. #define SAME 1
  887. #define DIFFERENT 2
  888.  
  889. #ifdef DEBUG_COMPRESSING_STATE_MACHINE
  890.         sendString((StringPtr)"\p\n\rcompressString(lineLength=");
  891.         NumToString((long)lineLength, String);
  892.         sendString(String);
  893.         sendString((StringPtr)"\p, bitLine=");
  894.         NumToString((long)(bitLine), String);
  895.         sendString(String);
  896.         sendString((StringPtr)"\p, compressedLineLength=");
  897.         NumToString((long)(compressedLineLength), String);
  898.         sendString(String);
  899.         sendString((StringPtr)"\p, compressedBitLine=");
  900.         NumToString((long)(compressedBitLine), String);
  901.         sendString(String);
  902.         sendString((StringPtr)"\p);\n\r");
  903. #endif DEBUG_COMPRESSING_STATE_MACHINE
  904.  
  905.     i = 0;  /* input buffer */
  906.     k = 0;  /* output buffer */
  907.     m = 0;  /* number of bytes in a group (max. 128) */
  908.     state = INITIAL;
  909.     *dirty = FALSE;
  910.     
  911.     while (TRUE) {
  912.         if (bitLine[i] != (unsigned char)0)
  913.             *dirty = TRUE;
  914.  
  915. #ifdef DEBUG_COMPRESSING_STATE_MACHINE
  916.         sendString((StringPtr)"\pstate=");
  917.         NumToString((long)state, String);
  918.         sendString(String);
  919.         sendString((StringPtr)"\p, i=");
  920.         NumToString((long)i, String);
  921.         sendString(String);
  922.         sendString((StringPtr)"\p, bitLine[i]=");
  923.         NumToString((long)(bitLine[i]), String);
  924.         sendString(String);
  925.         sendString((StringPtr)"\p, bitLine[i+1]=");
  926.         NumToString((long)(bitLine[i+1]), String);
  927.         sendString(String);
  928.         sendString((StringPtr)"\p, k=");
  929.         NumToString((long)k, String);
  930.         sendString(String);
  931.         sendString((StringPtr)"\p, m=");
  932.         NumToString((long)m, String);
  933.         sendString(String);
  934.         sendString((StringPtr)"\p\n\r");
  935. #endif DEBUG_COMPRESSING_STATE_MACHINE
  936.  
  937.         switch (state) {
  938.             case INITIAL: {
  939.                 if (i >= lineLength)
  940.                     goto outOfWhile;  /* input buffer finished */
  941.                     
  942.                 if (i == (lineLength-1)) {
  943.                     compressedBitLine[k++] = 0;  /* 1 different byte */
  944.                     compressedBitLine[k++] = bitLine[i];
  945.                     goto outOfWhile;
  946.                 }
  947.             
  948.                 if (bitLine[i] == bitLine[i+1]) {
  949.                     state = SAME;
  950.                 }
  951.                 else {
  952.                     state = DIFFERENT;
  953.                 }
  954.                 break;
  955.             }  /* case INITIAL */
  956.             
  957.             case SAME: {
  958.                 if (i >= lineLength)
  959.                     goto outOfWhile;  /* input buffer finished */
  960.                     
  961.                 if (i == (lineLength-1)) {  /* ends with at least 2 same bytes */
  962.                     compressedBitLine[k++] = (unsigned char)(- m);  /* repeat count: 2 -> -1, 3 -> -2 etc. */
  963.                     compressedBitLine[k++] = bitLine[i];
  964.                     goto outOfWhile;  /* input buffer finished */
  965.                 }
  966.                 
  967.                 if (m >= 126) {
  968.                     compressedBitLine[k++] = (unsigned char)(- m);  /* repeat count: 2 -> -1, 3 -> -2 etc. */
  969.                     compressedBitLine[k++] = bitLine[i];  /* last same byte of this group */
  970.                     i++;  /* the first byte of next group (same/different, don't know yet) */
  971.                     m = 0;
  972.                     state = INITIAL;
  973.                     break;
  974.                 }
  975.                 
  976.                 if (bitLine[i] == bitLine[i+1]) {
  977.                     i++;
  978.                     m++;  /* Yippee, one more! */
  979.                     state = SAME;
  980.                     break;
  981.                 }
  982.                 else {
  983.                     compressedBitLine[k++] = (unsigned char)(- m);  /* repeat count: 2 -> -1, 3 -> -2 etc. */
  984.                     compressedBitLine[k++] = bitLine[i];  /* last same byte of this group */
  985.                     i++;  /* the first byte of next group (same/different, don't know yet) */
  986.                     m = 0;
  987.                     state = INITIAL;
  988.                     break;
  989.                 }
  990.                 break;
  991.             }  /* case SAME */
  992.             
  993.             case DIFFERENT: {
  994.                 if (i >= lineLength)
  995.                     goto outOfWhile;  /* input buffer finished */
  996.                     
  997.                 if (i == (lineLength-1)) {  /* line ends with at least two different bytes */
  998.                     i -= m;
  999.                     compressedBitLine[k++] = (unsigned char)(m);  /* char count: 2 -> 1, 3 -> 2 etc. */
  1000.                     for (;m>=0;m--)
  1001.                         compressedBitLine[k++] = bitLine[i++];
  1002.                     goto outOfWhile;  /* input buffer finished */
  1003.                 }
  1004.                 
  1005.                 if (m >= 126) {
  1006.                     i -= m;
  1007.                     compressedBitLine[k++] = (unsigned char)(m);  /* char count: 2 -> 1, 3 -> 2 etc. */
  1008.                     for (;m>=0;m--)
  1009.                         compressedBitLine[k++] = bitLine[i++];
  1010.                     m = 0;
  1011.                     state = INITIAL;
  1012.                     break;
  1013.                 }
  1014.                 
  1015.                 if (bitLine[i] != bitLine[i+1]) {
  1016.                     i++;
  1017.                     m++;  /* Yippee, one more! */
  1018.                     state = DIFFERENT;
  1019.                     break;
  1020.                 }
  1021.                 else {
  1022.                     i -= m;
  1023.                     compressedBitLine[k++] = (unsigned char)(m);  /* char count: 2 -> 1, 3 -> 2 etc. */
  1024.                     for (;m>=0;m--)
  1025.                         compressedBitLine[k++] = bitLine[i++];
  1026.                     m = 0;
  1027.                     state = INITIAL;
  1028.                     break;
  1029.                 }
  1030.                 break;
  1031.             }  /* case DIFFERENT */
  1032.         }  /* switch */
  1033.     }  /* while */
  1034.     
  1035. outOfWhile:
  1036.     *compressedLineLength = k;
  1037. }  /* compressBitLine */
  1038.